/* ----------------------------------------------------------------------------------------------------------------
 * PlatformMotion
 * ----------------------------------------------------------------------------------------------------------------
 */
package pure.engine.status.motion 
{
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import pure.engine.entity.IEntity;
	import pure.engine.status.Status;
	import pure.engine.system.ns_despair;
	import pure.engine.world.TiledWorldContent;
	import pure.engine.world.WorldManager;
	import pure.utils.DesN;

	use namespace ns_despair;
	
	/**
	 * 平台动作
	 * 
	 * @author Pure
	 * 
	 * 速度公式:
	 * ①s = v*t + 1/2*a*t*t
	 * 
	 * ========================================
	 *  移动  >>--- 加速 → 限最大速度
	 *        >>--- 加速 → 加速度&速度反比
	 * ========================================
	 *  跳跃  >>--- 普通
	 *        >>--- 移动速度决定
	 * ========================================
	 *  停止  >>--- 移动按键弹起状态
	 *  摩擦  >>--- 空中摩擦力小
	 * ========================================
	 *  重力
	 * ========================================
	 *  Tile碰撞
	 * ========================================
	 * 
	 */
public class PlatformMotion extends Status 
{	
	public function PlatformMotion(maxHoriVel:Number, gravity:Number = 16 , maxVerticalVel:Number = 140, forceFactor:Number = 0.1, frictionFactor:Number = 0.15)
	{
		this.maxHoriVel = maxHoriVel;
		this.gravity = gravity;
		this.maxVerticalVel = maxVerticalVel;
		
		_force = _maxHoriVel * forceFactor;
		_friction = _maxHoriVel * frictionFactor;

		setTicked(true);
	}

	
	
	override public function set entity(E:IEntity):void
	{
		super.entity = E;
		_world = E.worldContent as TiledWorldContent;
		tileSize = _world.tileSize;
	}
	
	
	//======================
	// MOTION
	//======================
	
	
	// 移动 - 'L × R × S'
	public function get motion():String { return _motion; }
	public function set motion(value:String):void
	{		
		var h_flag:uint;
		var o_flag:uint;

		if (_motion != value)
		{
			o_flag     =  m_flag & 0x00F;
			h_flag     =  m_flag & 0x0F0;
			_motion = value;
			switch(_motion)
			{
				case MS:
					if(h_flag == MOVE)
					{
						m_flag = (m_flag & 0xF0F) | DECE;
					}
					break;
					
				case ML:
					if (o_flag != L || h_flag != MOVE)
					{
						m_flag = (m_flag & 0xF00) | MOVE | L;
					}
					break;
					
				case MR:
					if (o_flag != R || h_flag != MOVE)
					{
						m_flag = (m_flag & 0xF00) | MOVE | R;
					}
					break;
			}
		}
	}
	
	
	// 方向
	public function get orientation():String
	{
		return ((m_flag & 0x00F) == L)?ML:MR;
	}
	
	
	// 刚减速
	public function get justDece():Boolean
	{
		return ((m_flagOld & 0x0F0) == MOVE) &&
		          ((m_flag & 0x0F0) == DECE);
	}
	

	// 刚停下
	public function get justStop():Boolean
	{
		return ((m_flagOld & 0x0F0) == DECE) &&
		          ((m_flag & 0x0F0) == STOP);
	}
	
	
	//======================
	// JUMP
	//======================
	
	
	/**
	 * 跳跃
	 * @param height       像素每秒
	 * @param duration     时间差
	 * 
	 * reference:s = v*t + 1/2*a*t*t
	 */
	public function jump(height:Number, duration:Number = 1.4):void
	{
		_yVel = ( 0.5 * _gravity * TICKS_PER_SECOND * duration * duration - height / duration ) * 0.1
		m_flag = (m_flag & 0x0FF) | UP;
	}

	

	// 是否在地面
	public function get fixed():Boolean
	{
		return (m_flag & 0xF00) == FIXED;
	}

	// 刚跳起
	public function get justJumped():Boolean
	{
		return ((m_flagOld & 0xF00) == FIXED) &&
		          ((m_flag & 0xF00) == UP);
	}
	
	// 跳跃中
	public function get jumping():Boolean
	{
		return (m_flag & 0xF00) == UP;
	}
	
	// 刚下落
	public function get justFalled():Boolean
	{
		return ((m_flagOld & 0xF00) == UP) &&
		          ((m_flag & 0xF00) == DOWN);
	}
	
	// 下落中
	public function get falling():Boolean
	{
		return (m_flag & 0xF00) == DOWN;
	}
	
	// 刚着陆
	public function get justLanded():Boolean 
	{
		return ((m_flagOld & 0xF00) == DOWN) &&
		          ((m_flag & 0xF00) == FIXED);
	}
	
	// 空中
	public function get airborne():Boolean
	{
		return (m_flag & 0xF00) != FIXED;
	}

	
	
	override public function onTick(elapsedT:Number):void 
	{
		var h_flag:uint;
		var o_flag:uint;
		var real:Rectangle;
		
		m_flagOld  =  m_flag;
		o_flag     =  m_flag & 0x00F;
		h_flag     =  m_flag & 0x0F0;
		
		// 加速&限速(水平)
		if (h_flag == MOVE)
		{
			if (o_flag == L)
			{
				_xVel -= _force;
				_xVel  = DesN.clamp(_xVel, -_maxHoriVel, 0 );
			}
			
			else if (o_flag == R)
			{
				_xVel += _force;
				_xVel  = DesN.clamp(_xVel, 0, _maxHoriVel);
			}
		}
		
		// 减速(摩擦)
		else if(h_flag == DECE)
		{
			if (_xVel > 0)
			{
				_xVel -= _friction;
				if (_xVel <= 0)
				{
					_xVel = 0;
					m_flag = (m_flag & 0xF0F) | STOP;
				}
			}
			else if (_xVel < 0)
			{
				_xVel += _friction;
				if (_xVel >= 0)
				{
					_xVel = 0;
					m_flag = (m_flag & 0xF0F) | STOP;
				}
			}
		}
		
		// 重力
		if (_yVel < 0 && (_yVel += _gravity) >= 0)
		{
			// 检测开始下落
			m_flag = (m_flag & 0x0FF) | DOWN;
		}
		else
		{
			_yVel += _gravity;
		}
		
		// 限速(垂直)
		if (_yVel >= _maxVerticalVel)
		{
			_yVel = _maxVerticalVel;
		}
		
		// 碰撞
		real           =  _entity.realRect;
		this.m_top     =  real.top;
		this.m_bottom  =  real.bottom;
		this.m_left    =  real.left;
		this.m_right   =  real.right;
		
		_checkTileCollision();
		if (_yVel > 0)
		{
			m_flag = (m_flag & 0x0FF) | DOWN;
		}
	}

	
	
	//======================
	// get/set
	//======================

	// 重力
	public function get gravity():Number { return _gravity * TICKS_PER_SECOND; }
	public function set gravity(value:Number):void 
	{ 
		_gravity = value / TICKS_PER_SECOND;
	}	
	
	// 垂直限速
	public function get maxVerticalVel():Number { return _maxVerticalVel * TICKS_PER_SECOND; }
	public function set maxVerticalVel(value:Number):void 
	{ 
		_maxVerticalVel = value / TICKS_PER_SECOND;
	}
	
	// 水平限速
	public function get maxHoriVel():Number { return _maxHoriVel * TICKS_PER_SECOND; }
	public function set maxHoriVel(value:Number):void 
	{ 
		_maxHoriVel = value / TICKS_PER_SECOND;
	}


	
	
	//======================
	// private
	//======================
	
	private function _checkTileCollision() : void
	{
		if (_yVel > _xVel)
		{
			_calculateVerticalCollision();
			_calculateHorizontalCollision();
		}
		else
		{
			_calculateHorizontalCollision();
			_calculateVerticalCollision();
		}
	}
	
	private function _calculateVerticalCollision() : void
	{
		_getCorners(0, this._yVel);
		
		if (this._yVel < 0)
		{
			if (this.upLeft > 0 || this.upRight > 0)
			{
				_entity.y += Math.ceil(cachedTop / tileSize) * tileSize - m_top + 0.01;
				this._yVel = 0;
			}
			else
			{
				_entity.y += _yVel;
			}
		}
		else if (this._yVel > 0)
		{
			if (this.downLeft > 0 || this.downRight > 0)
			{
				_entity.y += Math.floor(cachedBottom / tileSize) * tileSize - m_bottom - 0.01;
				this._yVel = 0;
				m_flag = (m_flag & 0x0FF) | FIXED;
			}
			else
			{
				_entity.y += _yVel;
			}
		}
	}
	
	private function _calculateHorizontalCollision() : void
	{
		_getCorners(this._xVel, 0);
		
		if (this._xVel < 0)
		{
			if (this.upLeft > 0 || this.downLeft > 0)
			{
				_entity.x += Math.ceil(cachedLeft / tileSize) * tileSize - m_left + 0.01;
				this._xVel = 0;
			}
			else
			{
				_entity.x += _xVel;
			}
		}
		else if (this._xVel > 0)
		{
			if (this.upRight > 0 || this.downRight > 0)
			{
				_entity.x += Math.floor(cachedRight / tileSize) * tileSize - m_right - 0.01;
				this._xVel = 0;
			}
			else
			{
				_entity.x += _xVel;
			}
		}
	}

	private function _getCorners(vx:Number, vy:Number) : void
	{
		cachedLeft    =  m_left + vx;
		cachedRight   =  m_right + vx;
		cachedTop     =  m_top + vy;
		cachedBottom  =  m_bottom + vy;
		upLeft        =  _world.getLandform(cachedLeft,cachedTop);
		upRight       =  _world.getLandform(cachedRight,cachedTop);
		downLeft      =  _world.getLandform(cachedLeft,cachedBottom);
		downRight     =  _world.getLandform(cachedRight,cachedBottom);
	}

	
	

	//======================
	// Enumeration
	//======================
	
	public static const TICKS_PER_SECOND:uint = 30;
	
	protected static const L:uint         =  0x001;
	protected static const R:uint         =  0x002;
	
	protected static const STOP:uint      =  0x010;
	protected static const DECE:uint      =  0x020;
	protected static const MOVE:uint      =  0x040;
		
	protected static const UP:uint        =  0x100;
	protected static const DOWN:uint      =  0x200;
	protected static const FIXED:uint     =  0x400;
	
	
	public static const MS:String      =  'S';
	public static const ML:String      =  'L';
	public static const MR:String      =  'R';
	
	
	//======================
	// Member
	//======================
	
	protected var _xVel:Number = 0;
	protected var _yVel:Number = 0;
	protected var _force:Number;
	protected var _friction:Number;
	
	protected var _facing:uint;
	
	protected var _maxHoriVel:Number;
	protected var _gravity:Number;    // 像素每毫秒
	protected var _maxVerticalVel:Number;
	
	protected var _motion:String;
	protected var m_flag:uint = DOWN | STOP | R;
	protected var m_flagOld:uint;
	
	protected var _world:TiledWorldContent;
	
	protected var tileSize:int, m_top:Number, m_bottom:Number, m_left:Number, m_right:Number;
	protected var cachedTop:Number, cachedBottom:Number, cachedLeft:Number, cachedRight:Number;
	
	protected var upLeft:int;
	protected var upRight:int;
	protected var downLeft:int;
	protected var downRight:int;
	
	
}

}